<template>
  <PageWrapper>
    <!-- 笔记 -->
    <CollapseContainer class="mt-5" title="expose">
      <markdown-viewer :value="getDoc()" />
    </CollapseContainer>

    <CollapseContainer class="mt-5" title="expose 用法示例">
      <Father />

      <template #action>
        <Icon
          icon="ant-design:code-outlined"
          class="mr-1"
          :size="26"
          @click="openSource('expose 用法示例', getBasic())"
        />
      </template>
    </CollapseContainer>

    <!-- 源码组件 -->
    <SourceDrawer ref="sourceDrawer" />
  </PageWrapper>
</template>
<script lang="ts" setup>
  import { CollapseContainer } from '@/components/Container';
  import { MarkdownViewer } from '@/components/Markdown';
  import { PageWrapper } from '@/components/Page';
  import SourceDrawer from '@/learn/SourceDrawer.vue';
  import { ref } from 'vue';
  import { Icon } from '@/components/Icon';
  import Father from './Father.vue';

  // 定义子组件引用
  let sourceDrawer = ref<any>(null);
  function openSource(title: string, content: string) {
    sourceDrawer.value.open({ title, content, width: '60%' });
  }
</script>

<script lang="ts">
  function getDoc() {
    return `
* expose 可以实现 父调子元素，也可以实现 子调父元素，但更多的应用场景还是 父调子方法。
* 典型应用场景:
  * 子组件将逻辑全部封装到自己文件中，只对外暴露操作方法。
* 注意事项:
  * 暴露语法为{xx,xx} 而不是 [xx,xx]
  * 如果暴露数据，则一定要暴露数组或对象类型，如果暴露基本类型，会导致失去响应式
* 子传父步骤:
  * 子组件通过 defineExpose({aaa,bbb}) 将当前组件中的元素 (对象、方法) 暴露出去
  * 父组件使用标签时，指定引用对象名 <sub ref='subRef' \/\>
  * 父组件 js 通过 subRef.abb 方式调用子组件方法, 模板中不能直接使用 $subRef 对象
`;
  }

  function getBasic() {
    return `
### 父组件
\`\`\`html
<template>
  <div class="mt-5 bg-gray-300 p-5" title="父组件" :can-expan="false">
    <h3>父组件</h3>
    <ul>
      <li>子组件 age:{{ age }}<Button @click="invokeUpdateAge">修改</Button>失去了响应式</li>
      <li>子组件 names:{{ names }}<Button @click="invokeUpdateName">修改</Button>响应式</li>
      <li>子组件 address:{{ address }}<Button @click="invokeChangeAddress">修改</Button>响应式</li>
    </ul>

    <Child ref="childRef" />
  </div>
</template>

\<script lang="ts" setup\>
import Child from './Child.vue';
  import { ref, reactive, onMounted } from 'vue';
  import { Button } from '@/components/Button';

  // childRef 作为子组件的引用，可以使用 ref 也可以使用 reactive
  let childRef = ref<any>(null);

  let names = ref(null);
  let age = ref(null);
  let address = reactive({});

  onMounted(() => {
    // setup 方法中，不能直接使用 childRef 中的属性，需要等待组件加载完成后，才能使用。
    // 这种方式，name 和 age 将失去响应式。
    names.value = childRef.value.names;
    age.value = childRef.value.age;
    address = childRef.value.address;
    console.log('age:', childRef.value.age);
    console.log('name:', childRef.value.names);
    console.log('address:', childRef.value.address);

    console.log('组件加载完成....', childRef);
  });

  function invokeUpdateName() {
    childRef.value.changeName();
    // js 中能感受到子组件中的数据变化，childRef 是响应式的。但是在页面绑定中，将失去响应式
    console.log('子组件名称:', childRef.value.names);
  }

  function invokeUpdateAge() {
    childRef.value.changeAge();
  }
  function invokeChangeAddress() {
    childRef.value.changeAddress();
  }
\</script\>
\`\`\`

### 子组件
\`\`\`html
<template>
  <div class="mt-5 bg-blue-200 p-5" title="子组件" :can-expan="false">
    <h3>子组件</h3>
    <ul>
      <li> names:{{ names }} </li>
      <li> age:{{ age }} </li>
      <li> address:{{ address }} </li>
    </ul>
  </div>
</template>

\<script lang="ts" setup\>
import { ref, reactive } from 'vue';

let age = ref(20);
let names = ref(['zhangsna']);
let address = reactive({
  provience: '河南',
  city: '郑州',
});

function changeName() {
  names.value.push('a');
}

function changeAge() {
  age.value += 1;
}
function changeAddress() {
  address.city += '-@';
}

defineExpose({ changeName, changeAge, changeAddress, names, age, address });

\</script\>
\`\`\`
`;
  }
</script>

<style lang="less" scoped>
  button {
    margin-left: 5px;
  }
</style>
